package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/bwmarrin/snowflake"
	"github.com/geniuscirno/uploader/config"
	"github.com/gorilla/mux"
)

const (
	EStatusSuccess int = iota
	EStatusFail
	EStatusUnknow
)

type Error struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

type UploadResponse struct {
	Url string `json:"url"`
}

func saveUploadedFile(file *multipart.FileHeader, dst string) error {
	src, err := file.Open()
	if err != nil {
		return err
	}
	defer src.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, src)
	return err
}

func uploadToOSS(fileName string, path string) error {
	bucket, err := client.Bucket(cfg.OSS.Bucket)
	if err != nil {
		return err
	}

	err = bucket.PutObjectFromFile(fileName, path)
	if err != nil {
		return err
	}

	return nil
}

func isImage(ext string) bool {
	for _, v := range []string{".png", ".jpg", ".gif", ".jpeg", ".bmp"} {
		if v == ext {
			return true
		}
	}
	return false
}

func URLPathJoin(s string, l string) (string, error) {
	u, err := url.Parse(s)
	if err != nil {
		return "", err
	}
	u.Path = path.Join(u.Path, l)
	return u.String(), nil
}

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	uploadPath := vars["path"]
	render := json.NewEncoder(w)

	_, fh, err := r.FormFile("file")
	if err != nil {
		render.Encode(&Error{Code: EStatusFail, Msg: err.Error()})
		return
	}

	fileName := strings.TrimRight(node.Generate().Base64(), "=")
	ext := path.Ext(fh.Filename)
	if ext != fh.Filename {
		fileName += ext
	}

	savePath := path.Join(cfg.FileRoot, fileName)
	fmt.Println("savePath", savePath)
	if err := saveUploadedFile(fh, savePath); err != nil {
		render.Encode(&Error{Code: EStatusFail, Msg: err.Error()})
		return
	}

	uploadPath = path.Join(uploadPath, fileName)
	fmt.Println("uploadPath", uploadPath)

	fmt.Println("ext", ext)

	if err := uploadToOSS(uploadPath, savePath); err != nil {
		render.Encode(&Error{Code: EStatusFail, Msg: err.Error()})
		return
	}

	if err := os.Remove(savePath); err != nil {
		render.Encode(&Error{Code: EStatusFail, Msg: err.Error()})
		return
	}

	url, err := URLPathJoin(cfg.AccessPoint, uploadPath)
	if err != nil {
		url = uploadPath
	}

	render.Encode(&UploadResponse{Url: url})

}

var (
	node   *snowflake.Node
	cfg    *config.Config
	client *oss.Client
)

var (
	cfgFile string
)

func main() {
	flag.StringVar(&cfgFile, "c", "./config.toml", "config file")
	flag.Parse()

	var (
		err error
	)

	r := mux.NewRouter()

	cfg, err = config.FromFile(cfgFile)
	if err != nil {
		log.Fatal(err)
		return
	}

	client, err = oss.New(cfg.OSS.Endpoint, cfg.OSS.AccessID, cfg.OSS.AccessSecret)
	if err != nil {
		log.Fatal(err)
		return
	}

	node, err = snowflake.NewNode(cfg.Node)
	if err != nil {
		log.Fatal(err)
		return
	}

	r.HandleFunc("/{path:.*}", uploadHandler)
	http.Handle("/", r)

	log.Fatal(http.ListenAndServe(cfg.Address, nil))
}
